﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace ControlLibrary.StyledTextBox
{
    public class AlphaRichTextBox : RichTextBox
    {
        private AlphaPanel APanel;
        private Timer BlinkCaretTimer;
        private Color CaretColorInternal;
        private PointF CaretPosition;
        private Bitmap ClientRegionBitmap;
        private bool Ctrl;
        private bool Dlt;
        private bool DrawCaret;
        private bool DrawingCaret;
        private int InternalAlphaAmount;
        private Color InternalAlphaBackColor;
        private bool NewLine;
        protected internal Delegate STClientDel;
        private bool Scrolling;
        private RichTextInformationCollection SelectedRTInfoInternal;
        private Utilities TBUtils;
        private int TmpSelLength;
        private int TmpSelStart;
        private bool Updating;
        private Container components;

        public AlphaRichTextBox()
        {
            InitializeComponent();
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), Description("The Alpha Amount, or transparency amount, applied to the background."), Browsable(true), Category("Appearance")]
        public int AlphaAmount
        {
            get { return InternalAlphaAmount; }
            set
            {
                if ((value > 0xff) || (value < 0))
                {
                    throw new Exception("AlphaAmount must be between 0 and 255!");
                }
                InternalAlphaAmount = value;
                UpdateRegion();
                APanel.Refresh();
            }
        }

        [Description("The visible background color for the AlphaTextBox."), Category("Appearance"), Browsable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public Color AlphaBackColor
        {
            get { return InternalAlphaBackColor; }
            set
            {
                for (int i = 0; i < SelectedRTInfoInternal.Count; i++)
                {
                    if (SelectedRTInfoInternal[i].BackColorInternal.ToArgb() == InternalAlphaBackColor.ToArgb())
                    {
                        SelectedRTInfoInternal[i].BackColorInternal = value;
                    }
                }
                for (int j = 0; j < TextLength; j++)
                {
                    base.Select(j, 1);
                    if (SelectionBackColor.ToArgb() == InternalAlphaBackColor.ToArgb())
                    {
                        SelectionBackColor = value;
                    }
                }
                base.Select(TmpSelStart, TmpSelLength);
                InternalAlphaBackColor = value;
                UpdateRegion();
                APanel.Refresh();
            }
        }

        public override Color BackColor
        {
            get { return base.BackColor; }
            set { base.BackColor = Color.Transparent; }
        }

        public Color CaretColor
        {
            get { return CaretColorInternal; }
            set
            {
                CaretColorInternal = value;
                if (!Focused)
                {
                    base.Focus();
                }
            }
        }

        [Browsable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Description(""), Category("Appearance")]
        public RichTextInformationCollection SelectedRTInfo
        {
            get { return SelectedRTInfoInternal; }
        }

        [Category("Appearance"), Browsable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Description("")]
        public Color SelectionBackColor
        {
            get
            {
                Color empty = Color.Empty;
                Color back = Color.Empty;
                TBUtils.GetRTHighlight(ref back, ref empty, Color.Empty);
                return back;
            }
            set
            {
                if (!Focused)
                {
                    base.Focus();
                }
                TBUtils.SetRTHighlight(value, SelectionColor);
                for (int i = 0; i < SelectedRTInfoInternal.Count; i++)
                {
                    SelectedRTInfoInternal[i].BackColorInternal = value;
                }
            }
        }

        public Color SelectionColor
        {
            get { return base.SelectionColor; }
            set
            {
                if (!Focused)
                {
                    base.Focus();
                }
                base.SelectionColor = value;
                for (int i = 0; i < SelectedRTInfoInternal.Count; i++)
                {
                    SelectedRTInfoInternal[i].ForeColorInternal = value;
                }
                TBUtils.SetRTHighlight(SelectionBackColor, SelectionColor);
            }
        }

        public Font SelectionFont
        {
            get { return base.SelectionFont; }
            set
            {
                if (!Focused)
                {
                    base.Focus();
                }
                for (int i = 0; i < SelectedRTInfoInternal.Count; i++)
                {
                    SelectedRTInfoInternal[i].FontInternal = value;
                }
                base.SelectionFont = value;
                UpdateRegion();
            }
        }

        private void BlinkCaretTimer_Elapsed(object sender, EventArgs eventArgs)
        {
            if ((!Updating && !DrawingCaret) && !Scrolling)
            {
                DrawCaret = !DrawCaret;
                DrawCaretToBitmap();
            }
        }

        public void Copy()
        {
            var lParams = (IntPtr) TBUtils.SendMessageToMaster(TBUtils.EM_SETEVENTMASK, IntPtr.Zero, IntPtr.Zero, 1);
            TBUtils.SendMessageToMaster(TBUtils.WM_SETREDRAW, IntPtr.Zero, IntPtr.Zero, -1);
            RichTextInformationCollection selectedRTInfoInternal = SelectedRTInfoInternal;
            int tmpSelStart = TmpSelStart;
            int tmpSelLength = TmpSelLength;
            base.Select(TextLength, 0);
            WriteText();
            base.Select(tmpSelStart, tmpSelLength);
            Clipboard.SetDataObject(base.SelectedRtf, true);
            SelectedRTInfoInternal = selectedRTInfoInternal;
            WriteText();
            TBUtils.SendMessageToMaster(TBUtils.WM_SETREDRAW, (IntPtr) 1, IntPtr.Zero, -1);
            TBUtils.SendMessageToMaster(TBUtils.EM_SETEVENTMASK, IntPtr.Zero, lParams, -1);
        }

        private void DrawCaretToBitmap()
        {
            DrawingCaret = true;
            if (DrawCaret)
            {
                Graphics graphics = Graphics.FromImage(APanel.BackgroundImage);
                if (NewLine)
                {
                    graphics.FillRectangle((CaretColorInternal.ToArgb() == Color.Empty.ToArgb()) ? new SolidBrush(SelectionColor) : new SolidBrush(CaretColorInternal), CaretPosition.X, CaretPosition.Y, SelectionFont.SizeInPoints/5f,
                                           SelectionFont.Height);
                    graphics.Dispose();
                }
                else
                {
                    graphics.FillRectangle((CaretColorInternal.ToArgb() == Color.Empty.ToArgb()) ? new SolidBrush(SelectionColor) : new SolidBrush(CaretColorInternal), CaretPosition.X, CaretPosition.Y, SelectionFont.SizeInPoints/5f,
                                           SelectionFont.Height);
                    graphics.Dispose();
                }
            }
            else
            {
                APanel.BackgroundImage = (Bitmap) ClientRegionBitmap.Clone();
            }
            APanel.Refresh();
            DrawingCaret = false;
        }

        private void InitializeComponent()
        {
            base.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            components = new Container();
            STClientDel = new SMDel(DefWndProc);
            TBUtils = new Utilities(STClientDel, this);
            DrawCaret = true;
            Updating = false;
            DrawingCaret = false;
            Scrolling = false;
            Ctrl = false;
            NewLine = false;
            APanel = new AlphaPanel(this);
            TmpSelStart = -1;
            BlinkCaretTimer = new Timer();
            BlinkCaretTimer.Interval = 500;
            BlinkCaretTimer.Tick += BlinkCaretTimer_Elapsed;

            BlinkCaretTimer.Enabled = false;
            SelectedRTInfoInternal = new RichTextInformationCollection();
            base.Controls.Add(APanel);
        }

        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            DrawCaret = true;
            BlinkCaretTimer.Start();
        }

        protected override void OnKeyUp(KeyEventArgs e)
        {
            base.OnKeyUp(e);
            Ctrl = false;
            UpdateRegion();
        }

        protected override void OnLostFocus(EventArgs e)
        {
            base.OnLostFocus(e);
            BlinkCaretTimer.Stop();
            DrawCaret = false;
            DrawCaretToBitmap();
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            Scrolling = false;
            UpdateRegion();
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            APanel.Size = base.ClientSize;
            if (ClientRegionBitmap != null)
            {
                ClientRegionBitmap.Dispose();
            }
            ClientRegionBitmap = new Bitmap(base.ClientSize.Width, base.ClientSize.Height);
            UpdateRegion();
        }

        protected override void OnVisibleChanged(EventArgs e)
        {
            base.OnVisibleChanged(e);
            if (!Focused)
            {
                DrawCaret = false;
                BlinkCaretTimer.Stop();
            }
            UpdateRegion();
        }

        public void Paste()
        {
            IDataObject dataObject = Clipboard.GetDataObject();
            if (dataObject.GetDataPresent(typeof (string)))
            {
                var data = (string) dataObject.GetData(typeof (string));
                if (data.StartsWith("{"))
                {
                    base.SelectedRtf = data;
                }
                else
                {
                    SelectedText = data;
                }
            }
            else
            {
                if (dataObject.GetDataPresent(typeof (Bitmap)))
                {
                    var bmp = (Bitmap) dataObject.GetData(typeof (Bitmap));
                    Clipboard.SetDataObject(TBUtils.MapColors(AlphaBackColor, Color.FromArgb((AlphaBackColor.R != 0xff) ? (AlphaBackColor.R + 1) : (AlphaBackColor.R - 1), AlphaBackColor.G, AlphaBackColor.B), bmp, true), true);
                }
                base.Paste();
            }
            UpdateRegion();
        }

        public void Paste(DataFormats.Format ClipFormat)
        {
            Paste();
        }

        private void SetCaret()
        {
            if (Text == "")
            {
                CaretPosition = new PointF(2f, 2f);
                DrawCaretToBitmap();
            }
            else
            {
                int num = base.SelectionStart + SelectionLength;
                bool flag = false;
                float width = 0f;
                string text = "";
                int num3 = 0;
                if ((num == TextLength) && (Text[Text.Length - 1] != '\n'))
                {
                    num--;
                }
                else if ((num == TextLength) && (Text[Text.Length - 1] == '\n'))
                {
                    flag = true;
                    num--;
                }
                num3 = ((IntPtr) TBUtils.SendMessageToMaster(TBUtils.EM_POSFROMCHAR, (IntPtr) num, IntPtr.Zero, 1)).ToInt32();
                if (TmpSelStart != TextLength)
                {
                    CaretPosition = new PointF((num3 & 0xff), ((num3 >> 0x10) & 0xff));
                }
                else
                {
                    Graphics graphics = base.CreateGraphics();
                    text = Text[num].ToString();
                    width = graphics.MeasureString(text, SelectionFont).Width;
                    CaretPosition = new PointF((num3 & 0xff) + (width/2f), ((num3 >> 0x10) & 0xff));
                    graphics.Dispose();
                }
                if (flag)
                {
                    CaretPosition = new PointF(2f, CaretPosition.Y + SelectionFont.Height);
                }
                else
                {
                    var start = (int) ((IntPtr) TBUtils.SendMessageToMaster(TBUtils.EM_LINEINDEX, (IntPtr) (-1), IntPtr.Zero, 1));
                    int num5 = start + ((int) ((IntPtr) TBUtils.SendMessageToMaster(TBUtils.EM_LINELENGTH, (IntPtr) start, IntPtr.Zero, 1)));
                    int height = 0;
                    Color empty = Color.Empty;
                    Color fore = Color.Empty;
                    TBUtils.GetRTHighlight(ref empty, ref fore, AlphaBackColor);
                    Font selectionFont = SelectionFont;
                    while (start < num5)
                    {
                        base.Select(start, 1);
                        if (SelectionFont.Height > height)
                        {
                            height = SelectionFont.Height;
                        }
                        start++;
                    }
                    base.Select(TmpSelStart, 0);
                    base.SelectionFont = selectionFont;
                    TBUtils.SetRTHighlight(empty, fore);
                    CaretPosition.Y = ((CaretPosition.Y + height) - (height/5)) - selectionFont.Height;
                }
                NewLine = flag;
                DrawCaretToBitmap();
            }
        }

        private void UpdateRegion()
        {
            if (!Updating)
            {
                var lParams = (IntPtr) TBUtils.SendMessageToMaster(TBUtils.EM_SETEVENTMASK, IntPtr.Zero, IntPtr.Zero, 1);
                TBUtils.SendMessageToMaster(TBUtils.WM_SETREDRAW, IntPtr.Zero, IntPtr.Zero, -1);
                Updating = true;
                WriteText();
                if (TmpSelLength == 0)
                {
                    WriteBitmap();
                }
                TBUtils.SendMessageToMaster(TBUtils.WM_SETREDRAW, (IntPtr) 1, IntPtr.Zero, -1);
                TBUtils.SendMessageToMaster(TBUtils.EM_SETEVENTMASK, IntPtr.Zero, lParams, -1);
                if (TmpSelLength > 0)
                {
                    WriteBitmap();
                }
                if ((DrawCaret && (TmpSelLength == 0)) && !Scrolling)
                {
                    SetCaret();
                }
                Updating = false;
            }
        }

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == TBUtils.WM_KEYDOWN)
            {
                int num = m.WParam.ToInt32();
                if ((num == 0x2e) && (SelectedRTInfoInternal.Count > 0))
                {
                    Dlt = true;
                    base.WndProc(ref m);
                }
                else if (num == 0x11)
                {
                    Ctrl = true;
                    base.WndProc(ref m);
                }
                else if ((num == 0x43) && Ctrl)
                {
                    Copy();
                    Ctrl = false;
                }
                else if ((num == 0x56) && Ctrl)
                {
                    Paste();
                    Ctrl = false;
                }
                else
                {
                    Ctrl = false;
                    base.WndProc(ref m);
                    Scrolling = false;
                    UpdateRegion();
                }
            }
            else if (m.Msg == TBUtils.WM_MOUSEMOVE)
            {
                int num2 = m.LParam.ToInt32();
                if (m.WParam.ToInt32() == TBUtils.MK_LBUTTON)
                {
                    if (Updating)
                    {
                        base.WndProc(ref m);
                    }
                    else
                    {
                        int x = num2 & 0xff;
                        int y = (num2 >> 0x10) & 0xff;
                        int start = base.GetCharIndexFromPosition(new Point(x, y)) + 1;
                        if (start > base.SelectionStart)
                        {
                            base.Select(base.SelectionStart, start - base.SelectionStart);
                        }
                        else
                        {
                            int length = start + ((TmpSelStart - start) + TmpSelLength);
                            if (((base.SelectionStart + SelectionLength) == TextLength) && (length > 0))
                            {
                                base.Select(start, length);
                            }
                            else if ((length - start) > 0)
                            {
                                base.Select(start, length - start);
                            }
                        }
                        UpdateRegion();
                    }
                }
                else
                {
                    base.WndProc(ref m);
                }
            }
            else if (m.Msg == TBUtils.WM_VSCROLL)
            {
                base.WndProc(ref m);
                int num8 = m.WParam.ToInt32();
                Scrolling = true;
                DrawCaret = false;
                if (((num8 & TBUtils.SB_LINEUP) == 0) || ((num8 & TBUtils.SB_LINEDOWN) == 0))
                {
                    WriteBitmap();
                }
            }
            else if (m.Msg == TBUtils.WM_MOUSEWHEEL)
            {
                base.WndProc(ref m);
                Scrolling = true;
                DrawCaret = false;
                WriteBitmap();
            }
            else
            {
                base.WndProc(ref m);
            }
        }

        private void WriteBitmap()
        {
            base.BackColor = AlphaBackColor;
            var image = new Bitmap(base.ClientSize.Width, base.ClientSize.Height);
            Graphics g = Graphics.FromImage(image);
            int charIndexFromPosition = base.GetCharIndexFromPosition(new Point(2, 2));
            int endChar = base.GetCharIndexFromPosition(new Point(base.ClientSize.Width, base.ClientSize.Height)) + 1;
            TBUtils.FormatRange(g, charIndexFromPosition, endChar);
            ClientRegionBitmap = TBUtils.MapRichTextColors(AlphaBackColor, Color.FromArgb(AlphaAmount, AlphaBackColor), image, true);
            base.BackColor = Color.Transparent;
            APanel.BackgroundImage = (Bitmap) ClientRegionBitmap.Clone();
        }

        private void WriteText()
        {
            int selectionStart = base.SelectionStart;
            int selectionLength = SelectionLength;
            if (selectionLength > 0)
            {
                DrawCaret = false;
                BlinkCaretTimer.Stop();
            }
            else if (Focused)
            {
                DrawCaret = true;
                BlinkCaretTimer.Start();
            }
            if ((SelectedRTInfoInternal.Count != 0) && !Dlt)
            {
                for (int i = 0; i < TmpSelLength; i++)
                {
                    int start = i + TmpSelStart;
                    base.Select(start, 1);
                    TBUtils.SetRTHighlight(SelectedRTInfoInternal[i].BackColor, SelectedRTInfoInternal[i].ForeColor);
                }
                base.Select(selectionStart, selectionLength);
            }
            TmpSelStart = selectionStart;
            TmpSelLength = selectionLength;
            if ((selectionLength > 0) && !Dlt)
            {
                SelectedRTInfoInternal.Clear();
                for (int j = 0; j < selectionLength; j++)
                {
                    base.Select(j + selectionStart, 1);
                    Color empty = Color.Empty;
                    Color back = Color.Empty;
                    Font fnt = null;
                    TBUtils.GetRTHighlight(ref back, ref empty, AlphaBackColor);
                    fnt = SelectionFont;
                    SelectedRTInfoInternal.Add(new RichTextInformation(fnt, empty, back));
                    TBUtils.SetRTHighlight(SystemColors.Highlight, SystemColors.HighlightText);
                }
            }
            else if (Dlt)
            {
                Color foreColor = SelectedRTInfoInternal[SelectedRTInfoInternal.Count - 1].ForeColor;
                Color backColor = SelectedRTInfoInternal[SelectedRTInfoInternal.Count - 1].BackColor;
                SelectedRTInfoInternal.Clear();
                SelectionColor = foreColor;
                SelectionBackColor = backColor;
                Dlt = false;
            }
            else
            {
                SelectedRTInfoInternal.Clear();
            }
            base.Select(TmpSelStart, TmpSelLength);
        }

        #region Nested type: AlphaPanel

        private class AlphaPanel : Panel
        {
            private readonly AlphaRichTextBox MasterTb;
            private readonly Utilities PUtils;
            protected internal readonly Delegate ToMasterDel;

            protected internal AlphaPanel(AlphaRichTextBox Master)
            {
                MasterTb = Master;
                base.Size = Master.Size;
                base.Location = new Point(0, 0);
                ToMasterDel = MasterTb.STClientDel;
                base.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
                base.SetStyle(ControlStyles.UserPaint, true);
                base.SetStyle(ControlStyles.DoubleBuffer, true);
                base.SetStyle(ControlStyles.Selectable, false);
                PUtils = new Utilities(ToMasterDel, MasterTb);
            }

            protected override void WndProc(ref Message m)
            {
                base.WndProc(ref m);
                if (m.Msg == PUtils.WM_MOUSEMOVE)
                {
                    PUtils.SendMessageToMaster(m.Msg, m.WParam, m.LParam, -1);
                }
                else if (m.Msg == PUtils.WM_LBUTTONDOWN)
                {
                    PUtils.SendMessageToMaster(m.Msg, m.WParam, m.LParam, -1);
                }
                else if (m.Msg == PUtils.WM_LBUTTONUP)
                {
                    PUtils.SendMessageToMaster(m.Msg, m.WParam, m.LParam, -1);
                }
                else if (m.Msg == PUtils.WM_LBUTTONDBLCLK)
                {
                    PUtils.SendMessageToMaster(m.Msg, m.WParam, m.LParam, -1);
                }
                else if (m.Msg == PUtils.WM_MOUSELEAVE)
                {
                    PUtils.SendMessageToMaster(m.Msg, m.WParam, m.LParam, -1);
                }
                else if (m.Msg == PUtils.WM_RBUTTONDOWN)
                {
                    PUtils.SendMessageToMaster(m.Msg, m.WParam, m.LParam, -1);
                }
                else if (m.Msg == PUtils.WM_MOUSEACTIVATE)
                {
                    PUtils.SendMessageToMaster(m.Msg, m.WParam, m.LParam, -1);
                }
            }

            #region Nested type: SMDel

            private delegate void SMDel(ref Message M);

            #endregion
        }

        #endregion

        #region Nested type: SMDel

        private delegate void SMDel(ref Message M);

        #endregion
    }
}